﻿namespace MyCoreLib
{
    using System;
    using System.Collections;
    using System.Data;

    public abstract class DataRecordEnumerator : DisposableBase, IEnumerator 
    {
        private IDataReader m_reader;

        private object m_current;

        private readonly int ResultSetIndexMax;

        public event EventHandler ResultSetIndexChanged;

        public DataRecordEnumerator(IDataReader reader, int resultSetIndexMax = int.MaxValue)
        {
            if (ReferenceEquals(reader, null))
            {
                throw new ArgumentNullException("reader");
            }

            if (reader.IsClosed)
            {
                throw new ArgumentException("reader is closed");
            }

            if (resultSetIndexMax < 1)
            {
                throw new ArgumentOutOfRangeException("resultSetIndexMax");
            }

            this.m_reader = reader;
            this.ResultSetIndex = 0;
            this.ResultSetIndexMax = resultSetIndexMax;
            this.RecordsAffected = -1;
        }


        public int ResultSetIndex { get; private set; }

        /// <summary>
        /// Gets the number of rows changed, inserted, or deleted by execution of the SQL statement.
        /// </summary>
        /// <value>
        /// The number of rows changed, inserted, or deleted; 0 if no rows were affected or the statement failed; and -1 for SELECT statements.
        /// </value>
        public int RecordsAffected { get; private set; }

        object IEnumerator.Current
        {
            get
            {
                return m_current;
            }
        }


        public bool MoveNext()
        {
            this.ThrowIfDisposed();

            if (ReferenceEquals(this.m_reader, null))
            {
                return false;
            }

            if (this.m_reader.Read())
            {
                m_current = ReadDataRecord(this.m_reader);
                return true;
            }

            this.m_current = null;

            while (this.m_reader.NextResult())
            {
                if (++ResultSetIndex >= this.ResultSetIndexMax)
                {
                    break;
                }
                OnResultSetIndexChanged();
                if (this.m_reader.Read())
                {
                    m_current = ReadDataRecord(this.m_reader);
                    return true;
                }
            }

            this.m_reader.Dispose();
            this.m_reader = null;
            ResultSetIndex = -1;
            RecordsAffected = this.m_reader.RecordsAffected;

            this.OnCompleted();

            return false;
        }

        void IEnumerator.Reset()
        {
            throw new NotSupportedException();
        }

        protected abstract object ReadDataRecord(IDataRecord record);

        protected abstract void OnCompleted();

        protected virtual void OnResultSetIndexChanged()
        {
            var handler = ResultSetIndexChanged;
            if (handler != null)
            {
                handler(this, EventArgs.Empty);
            }
        }
    }
}
